﻿//-----------------------------------------------------------------------------
// <copyright file="UIElementServices.cs" company="William E. Kempf">
//     Copyright (c) William E. Kempf.
// </copyright>
//-----------------------------------------------------------------------------

namespace Onyx.Windows
{
    using System;
    using System.Linq;
    using System.Windows;
    using System.Windows.Controls;
    using System.Windows.Input;
    using System.Windows.Markup;
    using Onyx.Reflection;

    /// <summary>
    /// Defines services for <see cref="UIElement"/> types.
    /// </summary>
#if WPF
    internal class UIElementServices : ServicesBase, ICommandTarget, IFocusSuggested, IDisplayMessage, ICommonDialogProvider
#endif
#if SILVERLIGHT
    internal class UIElementServices : ServicesBase, IDisplayMessage
#endif
    {
        /// <summary>
        /// The <see cref="UIElement"/> to provide services for.
        /// </summary>
        private readonly UIElement element;

        /// <summary>
        /// Initializes a new instance of the <see cref="UIElementServices"/> class.
        /// </summary>
        /// <param name="element">The <see cref="UIElement"/> to provide services for.</param>
        public UIElementServices(UIElement element)
        {
            this.element = element;
        }

#if WPF
        /// <summary>
        /// Gets the command bindings.
        /// </summary>
        /// <value>The command bindings.</value>
        CommandBindingCollection ICommandTarget.CommandBindings
        {
            get { return this.element.CommandBindings; }
        }

        /// <summary>
        /// Sets the focus to an element within the "view".
        /// </summary>
        /// <param name="key">The key used to locate the element within the "view".</param>
        /// <returns>
        /// <see langword="true"/> if the element with the specified <paramref name="key"/> was found and focus was
        /// succesfully set to it; otherwise <see langword="false"/>.
        /// </returns>
        bool IFocusSuggested.SetFocus(string key)
        {
            return ((IFocusSuggested)this).SetFocus(key, false);
        }

        /// <summary>
        /// Sets the focus to an element within the "view".
        /// </summary>
        /// <param name="key">The key used to locate the element within the "view".</param>
        /// <param name="selectAll"><see langword="true"/> if all of the contents of the selected element should be
        /// selected.</param>
        /// <returns>
        /// <see langword="true"/> if the element with the specified <paramref name="key"/> was found and focus was
        /// succesfully set to it; otherwise <see langword="false"/>.
        /// </returns>
        bool IFocusSuggested.SetFocus(string key, bool selectAll)
        {
            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException(Reflect.GetField(() => key).Name);
            }

            DependencyObject obj = this.element.GetLogicalDescendents().Where(d => View.GetName(d) == key).SingleOrDefault();
            if (obj == null)
            {
                INameScope nameScope = NameScope.GetNameScope(this.element);
                if (nameScope != null)
                {
                    obj = nameScope.FindName(key) as DependencyObject;
                }
            }

            IInputElement inputElement = obj as IInputElement;
            if (inputElement == null)
            {
                return false;
            }

            FocusManager.SetFocusedElement(obj, inputElement);
            if (selectAll)
            {
                SelectAll(obj);
            }

            return true;
        }


        /// <summary>
        /// Creates the open file dialog.
        /// </summary>
        /// <returns>
        /// A newly created <see cref="IOpenFileDialog"/> instance.
        /// </returns>
        IOpenFileDialog ICommonDialogProvider.CreateOpenFileDialog()
        {
            Window window = this.element as Window ?? this.element.GetLogicalAncestors().OfType<Window>().SingleOrDefault();
            if (window == null)
            {
                throw new InvalidOperationException();
            }

            return new OpenFileDialogImplementer(window);
        }

        /// <summary>
        /// Creates the save file dialog.
        /// </summary>
        /// <returns>
        /// A newly created <see cref="ISaveFileDialog"/> instance.
        /// </returns>
        ISaveFileDialog ICommonDialogProvider.CreateSaveFileDialog()
        {
            Window window = this.element as Window ?? this.element.GetLogicalAncestors().OfType<Window>().SingleOrDefault();
            if (window == null)
            {
                throw new InvalidOperationException();
            }

            return new SaveFileDialogImplementer(window);
        }

        /// <summary>
        /// Selects all content in the given <paramref name="element"/>.
        /// </summary>
        /// <param name="element">The element.</param>
        private static void SelectAll(DependencyObject element)
        {
            TextBox textBox = element as TextBox;
            if (textBox != null)
            {
                textBox.SelectAll();
                return;
            }

            ListBox listBox = element as ListBox;
            if (listBox != null && listBox.SelectionMode != SelectionMode.Single)
            {
                listBox.SelectAll();
            }
        }
#endif

        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(string messageBoxText)
        {
            return ((IDisplayMessage)this).ShowMessage(
                messageBoxText,
                string.Empty);
        }

        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <param name="caption">A <see cref="String"/> that specifies the title bar caption to display.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(string messageBoxText, string caption)
        {
            return ((IDisplayMessage)this).ShowMessage(
                messageBoxText,
                caption,
                MessageBoxButton.OK);
        }

        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <param name="caption">A <see cref="String"/> that specifies the title bar caption to display.</param>
        /// <param name="button">A <see cref="MessageBoxButton"/> value that specifies which button or buttons to display.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(string messageBoxText, string caption, MessageBoxButton button)
        {
#if WPF
            return ((IDisplayMessage)this).ShowMessage(
                messageBoxText,
                caption,
                button,
                MessageBoxImage.None);
#endif
#if SILVERLIGHT
            return MessageBox.Show(
                messageBoxText,
                caption,
                button);
#endif
        }

#if WPF
        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <param name="caption">A <see cref="String"/> that specifies the title bar caption to display.</param>
        /// <param name="button">A <see cref="MessageBoxButton"/> value that specifies which button or buttons to display.</param>
        /// <param name="icon">A <see cref="MessageBoxImage"/> value that specifes the icon to display.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(
            string messageBoxText,
            string caption,
            MessageBoxButton button,
            MessageBoxImage icon)
        {
            return ((IDisplayMessage)this).ShowMessage(
                messageBoxText,
                caption,
                button,
                icon,
                MessageBoxResult.None);
        }

        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <param name="caption">A <see cref="String"/> that specifies the title bar caption to display.</param>
        /// <param name="button">A <see cref="MessageBoxButton"/> value that specifies which button or buttons to display.</param>
        /// <param name="icon">A <see cref="MessageBoxImage"/> value that specifes the icon to display.</param>
        /// <param name="defaultResult">A <see cref="MessageBoxResult"/> value that specifies the default result of the message box.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(
            string messageBoxText,
            string caption,
            MessageBoxButton button,
            MessageBoxImage icon,
            MessageBoxResult defaultResult)
        {
            return ((IDisplayMessage)this).ShowMessage(
                messageBoxText,
                caption,
                button,
                icon,
                defaultResult,
                MessageBoxOptions.None);
        }

        /// <summary>
        /// Displays a message to the user, typically as a message box.
        /// </summary>
        /// <param name="messageBoxText">A <see cref="String"/> that specifies the text to display.</param>
        /// <param name="caption">A <see cref="String"/> that specifies the title bar caption to display.</param>
        /// <param name="button">A <see cref="MessageBoxButton"/> value that specifies which button or buttons to display.</param>
        /// <param name="icon">A <see cref="MessageBoxImage"/> value that specifes the icon to display.</param>
        /// <param name="defaultResult">A <see cref="MessageBoxResult"/> value that specifies the default result of the message box.</param>
        /// <param name="options">A <see cref="MessageBoxOptions"/> value object that specifies the options.</param>
        /// <returns>
        /// A <see cref="MessageBoxResult"/> value that specifies which message box button was clicked by the user.
        /// </returns>
        MessageBoxResult IDisplayMessage.ShowMessage(
            string messageBoxText,
            string caption,
            MessageBoxButton button,
            MessageBoxImage icon,
            MessageBoxResult defaultResult,
            MessageBoxOptions options)
        {
            return MessageBox.Show(
                messageBoxText,
                caption,
                button,
                icon,
                defaultResult,
                options);
        }
#endif
    }
}
